package edu.cmu.aes.project1.app3;

/**
 This file: XMLProcessing.java
 Programmer: Prasanna Vaidyanathan (pvaidyan@andrew.cmu.edu)
 Course/Section: Architecting Enterprise Systems F11 A
 Assignment: Project 1
 Description: This file provides the capabilities to search models, view reviews, add review and sort 
 the data in XML file.
 Last Modified: 10/02/11
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xquery.*;

import javax.xml.parsers.*;
import javax.xml.xpath.*;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import edu.cmu.aes.project1.app2.XQueryUtils;

/**
 * Implements functions for processing, manipulating and presenting XML data to the user
 * @author Prasanna
 *
 */
public class XMLProcessing {

	/**
	 * @param args
	 * @throws XPathExpressionException
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws XQException
	 * @throws TransformerException
	 */
	public static void main(String[] args) throws XPathExpressionException,
			SAXException, IOException, ParserConfigurationException,
			InstantiationException, IllegalAccessException,
			ClassNotFoundException, XQException, TransformerException {

		XQueryUtils xqutil = new XQueryUtils();
		searchBrands(xqutil);

	}

	/**
	 * This function lists all the available brands and asks the user to choose
	 * a brand to continue
	 * 
	 * @param xqutil
	 *            contains the necessary class objects which will be used by the
	 *            XQuery API to manipulate data in XML
	 * @throws XQException
	 * @throws XPathExpressionException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws TransformerException
	 */
	public static void searchBrands(XQueryUtils xqutil) throws XQException,
			XPathExpressionException, ParserConfigurationException,
			SAXException, IOException, TransformerException {

		ArrayList<String> brands = new ArrayList<String>();
		System.out.println("Choose a brand from the list below");
		String es = "let $list := doc('output/app2/consolidated_data.xml')/models/model/manufacturer "
				+ "for $n in  distinct-values($list) "
				+ "return <manufacturer>{$n}</manufacturer>";
		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		Node node;
		NodeList nodeDetails = null;
		int itemnum = 1;
		while (xqutil.getResult().next()) {
			node = xqutil.getResult().getNode();
			nodeDetails = node.getChildNodes();
			brands.add(nodeDetails.item(0).getNodeValue());
			System.out.println(itemnum + "."
					+ nodeDetails.item(0).getNodeValue());
			itemnum++;
		}

		System.out.println();
		int choice = Integer.parseInt(xqutil.getInput().nextLine());
		brandsMenu(xqutil, brands.get(choice - 1));
	}

	/**
	 * After the user chooses the brand from the menu lit this function displays
	 * a menu with operations that can be performed for the brand such as View
	 * Models - View the List of models available in the chosen brand Sort -
	 * Sort the list of models data according to the constraint specified by the
	 * user
	 * 
	 * @param xqutil
	 * @param brand
	 * @throws XQException
	 * @throws XPathExpressionException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws TransformerException
	 */
	public static void brandsMenu(XQueryUtils xqutil, String brand)
			throws XQException, XPathExpressionException,
			ParserConfigurationException, SAXException, IOException,
			TransformerException {

		while (true) {
			System.out.println("Choose and option from below");
			System.out.println("1.View Models\n2.Sort\n3.Exit");
			int choice = Integer.parseInt(xqutil.getInput().nextLine());
			switch (choice) {
			case 1:
				viewModels(xqutil, brand);
				break;
			case 2:
				sortMenu(xqutil, brand);
				break;
			case 3:
				System.exit(0);
			default:
				System.out.println("Wrong choice. Please try again");
			}
		}
	}

	/**
	 * This function displays the List of available models in the brand chosen
	 * by the user
	 * 
	 * @param xqutil
	 * @param brand
	 * @throws XQException
	 * @throws XPathExpressionException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws TransformerException
	 */
	public static void viewModels(XQueryUtils xqutil, String brand)
			throws XQException, XPathExpressionException,
			ParserConfigurationException, SAXException, IOException,
			TransformerException {

		ArrayList<String> models = new ArrayList<String>();

		String es = "for $n in fn:doc('output/app2/consolidated_data.xml')/models/model "
				+ "where $n/manufacturer ='"
				+ brand
				+ "' "
				+ "return fn:data($n/@name)";
		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		System.out.println("Choose one model from the list below");
		int i = 1;
		while (xqutil.getResult().next()) {
			// retrieve the current item of the sequence as a String
			String str = xqutil.getResult().getAtomicValue();
			System.out.println(i + "." + str);
			i++;
			models.add(str);
		}
		System.out.println();

		int choice = Integer.parseInt(xqutil.getInput().nextLine());
		modelsMenu(xqutil, models.get(choice - 1));

	}

	/**
	 * After a Model is chosen from the List of models this functions displays
	 * the menu of operations that can be performed on the model such as View
	 * Details - View all the details of the model such as price, rating,
	 * frameset etc View Ratingt - View the ratings of a particular model View
	 * Reviews - View the reviews associated with the model Add Review - Add
	 * review to a model
	 * 
	 * @param xqutil
	 * @param model
	 * @throws XQException
	 * @throws XPathExpressionException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws TransformerException
	 */
	public static void modelsMenu(XQueryUtils xqutil, String model)
			throws XQException, XPathExpressionException,
			ParserConfigurationException, SAXException, IOException,
			TransformerException {

		while (true) {
			System.out.println("Choose and option from below");
			System.out
					.println("1.View details\n2.Add Review\n3.Exit");
			int choice = Integer.parseInt(xqutil.getInput().nextLine());
			switch (choice) {
			case 1:
				viewDetails(xqutil, model);
				break;
			case 2:
				addReview(xqutil, model);
				break;
			case 3:
				System.exit(0);
			default:
				System.out.println("Wrong choice. Please try again");
			}
		}

	}

	/**
	 * This function prints all the details of the model such as price,
	 * manufacturer, raating etc
	 * 
	 * @param xqutil
	 * @param model
	 * @throws XQException
	 */
	public static void viewDetails(XQueryUtils xqutil, String model)
			throws XQException {

		String es = "for $n in doc('output/app2/consolidated_data.xml')/models/model "
				+ "where $n/@name ='" + model + "' " + "return $n/*";
		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		
		HTMLTransformation.transform(xqutil.getResult(), true, model);
		

	}

	/**
	 * This function prints the rating of a particular model
	 * 
	 * @param xqutil
	 * @param model
	 * @throws XQException
	 */
	public static void viewRating(XQueryUtils xqutil, String model)
			throws XQException {

		String es = "for $n in doc('output/app2/consolidated_data.xml')/models/model "
				+ "where $n/@name ='" + model + "' " + "return $n/rating";
		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		Node node;
		NodeList nodeDetails = null;
		while (xqutil.getResult().next()) {
			node = xqutil.getResult().getNode();
			nodeDetails = node.getChildNodes();

			for (int i = 0; i < nodeDetails.getLength(); i++) {
				// System.out.println(nodeDetails.item(i).getAttributes().getNamedItem("name").getNodeValue()+":"+nodeDetails.item(i).getTextContent());

				System.out.println(nodeDetails.item(i).getNodeName() + ":"
						+ nodeDetails.item(i).getTextContent());
			}

		}

		System.out.println();

	}

	/**
	 * This function prints the reviews of a particular model
	 * 
	 * @param xqutil
	 * @param model
	 * @throws XQException
	 */
	public static void viewReview(XQueryUtils xqutil, String model)
			throws XQException {

		String es = "for $n in doc('output/app2/consolidated_data.xml')/models/model "
				+ "where $n/@name ='" + model + "' " + "return $n/review";
		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		Node node;
		NodeList nodeDetails = null;
		while (xqutil.getResult().next()) {
			node = xqutil.getResult().getNode();
			nodeDetails = node.getChildNodes();

			for (int i = 0; i < nodeDetails.getLength(); i++) {
				// System.out.println(nodeDetails.item(i).getAttributes().getNamedItem("name").getNodeValue()+":"+nodeDetails.item(i).getTextContent());

				System.out.println("Review :" + nodeDetails.item(i).getTextContent());
			}

		}

		System.out.println();

	}

	/**
	 * Add a review to the list of existing reviews for a model
	 * 
	 * @param xqutil
	 * @param model
	 * @throws XQException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 * @throws TransformerException
	 */
	public static void addReview(XQueryUtils xqutil, String model)
			throws XQException, ParserConfigurationException, SAXException,
			IOException, XPathExpressionException, TransformerException {

		System.out.println("Please enter the review to be added");
		String review = xqutil.getInput().nextLine();

		DocumentBuilderFactory domFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = (Document) builder
				.parse("output/app2/consolidated_data.xml");

		XPathFactory factory = XPathFactory.newInstance();
		XPath xpath = factory.newXPath();

		// title[@lang='eng']
		XPathExpression expr = xpath.compile("/models/model[@name='" + model
				+ "']");

		Object result = expr.evaluate(doc, XPathConstants.NODESET);
		NodeList nodes = (NodeList) result;
		Text a = ((org.w3c.dom.Document) doc).createTextNode(review);
		
		Node reviewNode = null;
		Element p = null;
//		NodeList itemList = nodes.item(0).getChildNodes();
//		for(int i = 0;i<itemList.getLength(); i++){
//			Node node = itemList.item(i);
//			if("review".equals(node.getNodeName())){
//				reviewNode = node;
//				reviewNode.setTextContent(review);
//				break;
//			}
//		}
//		if(reviewNode == null){
			p = ((org.w3c.dom.Document) doc).createElement("review");
			reviewNode = p;
			reviewNode.appendChild(a);
//		}
		
		nodes.item(0).appendChild(reviewNode);
		//nodes.item(0).getParentNode().insertBefore(p, nodes.item(0));

		writeXmlFile(doc, "output/app2/consolidated_data.xml");

		System.out.println("Review is added to the xml");

	}

	/**
	 * This method writes a DOM document to a file
	 * 
	 * @param doc
	 * @param filename
	 */
	public static void writeXmlFile(Document doc, String filename) {
		try {
			// Prepare the DOM document for writing
			Source source = new DOMSource(doc);

			// Prepare the output file
			File file = new File(filename);
			File fileDirectory = new File(file.getParent());
			if (!fileDirectory.exists())
				fileDirectory.mkdir();

			Result result = new StreamResult(file);

			// Write the DOM document to the file
			Transformer xformer = TransformerFactory.newInstance()
					.newTransformer();
			xformer.transform(source, result);
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * This function provides the list of menu options available for sort
	 * 
	 * @param xqutil
	 * @param brand
	 * @throws XQException
	 */
	public static void sortMenu(XQueryUtils xqutil, String brand)
			throws XQException {

		while (true) {
			System.out.println("Choose and option from below");
			System.out.println("1.Rating Score\n2.Price\n3.Exit");
			int choice = Integer.parseInt(xqutil.getInput().nextLine());
			switch (choice) {
			case 1:
				sortModels(xqutil, brand, "score", "number");
				break;
			case 2:
				sortModels(xqutil, brand, "price", "number");
				break;
			case 3:
				System.exit(0);
			default:
				System.out.println("Wrong choice. Please try again");
			}
		}
	}

	/**
	 * This function sorts the list of available models according to the sort
	 * Condition selected by the user such as Price,Rating etc
	 * 
	 * @param xqutil
	 * @param brand
	 * @param sortCondition
	 * @param type
	 * @throws XQException
	 */
	public static void sortModels(XQueryUtils xqutil, String brand,
			String sortCondition, String type) throws XQException {

		// sortCondition = "manufacturer";
		String es;
		if (type.equals("number")) {
			es = "for $n in fn:doc('output/app2/consolidated_data.xml')/models/model "
					+ "where $n/manufacturer ='"
					+ brand
					+ "' "
					+ "and "
					+ "fn:string(fn:number($n//"
					+ sortCondition
					+ ")) != 'NaN' "
					+ "order by xs:decimal($n//"
					+ sortCondition
					+ "),$n/@name "
					+ "return <result><model>{fn:data($n/@name)}</model><sortedvalue>{fn:data($n//"
					+ sortCondition + ")}</sortedvalue></result> ";
		} else {

			es = "for $n in fn:doc('output/app2/consolidated_data.xml')/models/model "
					+ "where $n/manufacturer ='"
					+ brand
					+ "' "
					+ "order by $n//"
					+ sortCondition
					+ " "
					+ "return <result><model>{fn:data($n/@name)}</model>"
					+ "<sortedvalue>{fn:data($n//"
					+ sortCondition
					+ ")}</sortedvalue></result> ";

		}

		// execute the XQuery expression
		xqutil.setResult(xqutil.getExpr().executeQuery(es));
		// process the result (sequence) iteratively

		System.out.println("Models in sorted order accroding to "
				+ sortCondition + "\n");

		HTMLTransformation.transform(xqutil.getResult(), false, sortCondition);
//		while (xqutil.getResult().next()) {
//
//			node = xqutil.getResult().getNode();
//			nodeDetails = node.getChildNodes();
//
//			for (int i = 0; i < nodeDetails.getLength(); i++) {
//				// System.out.println(nodeDetails.item(i).getAttributes().getNamedItem("name").getNodeValue()+":"+nodeDetails.item(i).getTextContent());
//				if (nodeDetails.item(i).getNodeName().equals("model"))
//					System.out
//							.print(nodeDetails.item(i).getTextContent() + "-");
//				else
//					System.out.print(nodeDetails.item(i).getTextContent());
//			}
//			System.out.println();
//		}
//
//		System.out.println();

	}

}


